package org.codetranslators.tools.visualization;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.io.CharArrayReader;
import java.io.File;
import java.io.FileWriter;
import java.util.HashMap;
import java.util.Vector;

import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.codetranslators.common.ControlFlowGraph;
import org.codetranslators.common.Temp;
import org.codetranslators.common.optimization.OptimizationContext;
import org.codetranslators.compiler.abstractassembly.AssemType;
import org.codetranslators.compiler.canon.Linearizer;
import org.codetranslators.compiler.intermrep.AssemblyInstruction;
import org.codetranslators.compiler.intermrep.CodeGenerator;
import org.codetranslators.compiler.tiger.Debug;
import org.codetranslators.compiler.tiger.ErrorHandler;
import org.codetranslators.compiler.tiger.abssyntree.Absyn;
import org.codetranslators.compiler.tiger.abssyntree.Symbol;
import org.codetranslators.compiler.tiger.abssyntree.TigerParser;
import org.codetranslators.compiler.tiger.lexer.Lexer;
import org.codetranslators.compiler.tiger.semantics.Level;
import org.codetranslators.compiler.tiger.semantics.SemanticAnalyzer;

public class MainPanel extends JPanel implements ChangeListener{
	
	private JPanel codePanel;
	private JPanel xmlPanel;
	private CFGPanel cfgPanel;
	private JScrollPane cfgScrollPane;
	private JPanel codeContainerTab;
	
	private JTabbedPane tabbedPane;
	private JTextPane codeTextPane;
	
	private GUIMediator guiMediator;
	
	public MainPanel(){
		
		guiMediator = new GUIMediator();
		
		setLayout(new BorderLayout());
		
		// Create the panel for the code view
		codePanel = new JPanel();
		codePanel.setLayout(new BorderLayout());
		codeTextPane = new JTextPane();
		JScrollPane jspCodeScroll = new JScrollPane(codeTextPane);
		codePanel.add(jspCodeScroll, BorderLayout.CENTER);
		
		// Create the panel for the xml view
		xmlPanel = new JPanel();
		xmlPanel.setBackground(Color.white);
		
		// Create the panel for the cfg view
		codeContainerTab = new JPanel();
		codeContainerTab.setLayout(new BorderLayout());
		
		cfgPanel = new CFGPanel(guiMediator);
		cfgScrollPane = new JScrollPane(cfgPanel);
		cfgScrollPane.setBackground(Color.white);
		
		codeContainerTab.add(cfgScrollPane, BorderLayout.CENTER);

		codeContainerTab.setBackground(Color.white);
		
		tabbedPane = new JTabbedPane();
		tabbedPane.add(codePanel, "Code Sample");
		tabbedPane.add(xmlPanel, "XML View");
		tabbedPane.add(codeContainerTab, "Control Flow Graph");
		tabbedPane.addChangeListener(this);
		
		add(tabbedPane, BorderLayout.CENTER);
	}
	
	public void stateChanged(ChangeEvent ce){
		ControlFlowGraph cfg = getControlFlowGraph();
		System.out.println("Control Flow Graph to be rendered: ");
		cfg.displayFlowGraph();
		if(tabbedPane.getSelectedComponent() == codeContainerTab){
			cfgPanel.setCfg(cfg);
			cfgPanel.setRecalculate(true);
			cfgPanel.repaint();
			
		}
	}
	
	private ControlFlowGraph getControlFlowGraph(){
		
		Temp.reset();
		
		Vector<ControlFlowGraph> cfgs = new Vector<ControlFlowGraph>(); // List of cfgs
		try{
			// Get the text from the code text pane
			String codeText = codeTextPane.getText();
			CharArrayReader strReader = new CharArrayReader(codeText.toCharArray());
			
			String filename = "VisualizationTest.tig";
		      
		    System.out.println("File: " + filename);
		 
			// Generate name of the output file
			File inFile = new File(filename);
			String inFileName = inFile.getName();
			int periodPos  = inFileName.indexOf(".");
			String outFileNameInterim =  inFileName.substring(0, periodPos) + "_intrm.s";    // for intermediate result
			  
			String outFileName = inFileName.substring(0, periodPos) + ".s";    // for final code after register allocation
			 
			File outFile = new File(outFileNameInterim);
			  
			// Lexical analysis and parsing
			TigerParser tp = new TigerParser(new Lexer(strReader));
			Object result = tp.parse().value;
			  
			// Semantic analysis
			System.out.println("Completed generating abstract syntax tree");
			SemanticAnalyzer semantAnalyzer = new SemanticAnalyzer();
			  
			Vector functionLevels = semantAnalyzer.translateAbstractTree((Absyn) result);  
			ErrorHandler errorHandler = ErrorHandler.getInstance();
			errorHandler.displayResult();
			 
			// Intermediate code generation
			Debug debug = new Debug();
			if(errorHandler.getNumErrors() != 0) 
				  return null;
			  
			  // No errors in semantic analysis; continue with intermediate code generation
			  // for each function
			  
			  Vector intermediateCodeListing = new Vector();
			  Vector finalCodeListing = new Vector();
			  
			  boolean compiledSuccessfully = true;
			  int numFuncs = functionLevels.size();
			  HashMap fNameVsNumRegsUsed = new HashMap();
			  Vector commonData = new Vector();
			  	  
			  for(int i = 0; i < numFuncs; i++){
				  Level fLevel = (Level) functionLevels.elementAt(i);
				  String functionName = fLevel.getLevelName().toString();
				  if(fLevel != null){
					  AssemType body = fLevel.getFunctionBody();
					  if(body != null)
					  {
						  System.out.println("FUNCTION:");
						  //debug.printAssem(body);
						  Linearizer linearizer = processWithLinearizer(debug, body, fLevel.getLevelName());
			
						  // Generate assembly code
			  
						  CodeGenerator codeGenerator = new CodeGenerator();
						  codeGenerator.generateCode(linearizer.getTrace());
						  Vector instList = codeGenerator.getInstructionsList();
						  intermediateCodeListing.addAll(instList);
						  writeInstructionsToFile(outFile, intermediateCodeListing);
			  
						  // Create a control flow graph from the instructions created above
						  ControlFlowGraph flowGraph = new ControlFlowGraph(instList, false);
						  
						  // After optimization using SSA form
						  OptimizationContext optimizationContext = new OptimizationContext(flowGraph);
		        		  optimizationContext.setSSAMethod();
		        		  flowGraph = optimizationContext.getOptimizedFlowGraph();
						  		  
						  cfgs.add(flowGraph);
					  }
				  }
			  }
		}
		catch(Exception e){
			e.printStackTrace();
		}
		
		return cfgs.elementAt(0);   // Return the first cfg found
	}
	
	private void writeInstructionsToFile(File outFile, Vector instList){
		  try
		  {
			  FileWriter fileWriter = new FileWriter(outFile);
			  int instNum = instList.size();
			  for(int i = 0; i < instNum; i++)
			  {
				  AssemblyInstruction instr = (AssemblyInstruction) instList.elementAt(i);
				  String linenum = "" + i + ") "; 
				  String str = linenum + instr.getAssemCode(false);
				  fileWriter.write(str);
			  }
			  fileWriter.flush();
			  fileWriter.close();
		  }
		  catch(Exception e)
		  {
			  e.printStackTrace();
		  }
	 }
	
	private Linearizer processWithLinearizer(Debug debug, AssemType body, Symbol levelName) {
		  Linearizer linearizer = new Linearizer(levelName.toString());
		  System.out.println("AFTER CANONIZING WITH LINEARIZER:");
		  linearizer.linearize(body);
		  debug.print(linearizer.getCanonized());
		  
		  System.out.println("BASIC BLOCKS WITH LIST GENERATED FROM LINEARIZER:");
		  linearizer.generateBasicBlocks();
		  debug.print(linearizer.getBasicBlocks());
		  
		  System.out.println("TRACE SCHEDULE WITH LINEARIZER:");
		  linearizer.generateTrace();
		  debug.print(linearizer.getTrace());  
		  
		  return linearizer;
	  }
}
class CFGPanel extends JPanel{
	private GUIMediator guiMediator;
	
	private boolean recalculate;
	private ControlFlowGraph cfg;
	
	public CFGPanel(GUIMediator guiMediator){
		setBackground(Color.white);
		this.guiMediator = guiMediator;
	}
	
	public void setRecalculate(boolean b){
		recalculate = b;
	}
	
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		if(guiMediator == null) return;
		
		if(recalculate){
			Dimension graphSize = guiMediator.updateDrawParameters(cfg, cfg.getStartNode(),g);
			setPreferredSize(graphSize);
			revalidate();
			recalculate = false;
		}
		
		guiMediator.drawGraph(g);
	}

	public void setCfg(ControlFlowGraph cfg) {
		this.cfg = cfg;
	}
}



